package com.zkt.practice.server.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.util.DateUtils;
import com.zkt.practice.api.common.PageInfo;
import com.zkt.practice.api.common.PageResult;
import com.zkt.practice.api.enums.CompleteStatusEnum;
import com.zkt.practice.api.enums.IsDeletedFlagEnum;
import com.zkt.practice.api.enums.SubjectInfoTypeEnum;
import com.zkt.practice.api.req.GetPracticeSubjectsReq;
import com.zkt.practice.api.req.GetUnCompletePracticeReq;
import com.zkt.practice.api.vo.*;
import com.zkt.practice.server.entity.dto.CategoryDTO;
import com.zkt.practice.server.entity.dto.PracticeSetDTO;
import com.zkt.practice.server.entity.dto.PracticeSubjectDTO;
import com.zkt.practice.server.entity.po.*;
import com.zkt.practice.server.mapper.*;
import com.zkt.practice.server.service.PracticeSetService;
import com.zkt.practice.server.utils.LoginUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;

/**
 * @author 赵开泰
 * @program jc-club
 * @date 2025/3/23
 * @description 练习套卷service层实现类
 **/

@Service
@Slf4j
public class PracticeSetServiceImpl implements PracticeSetService {
	
	@Resource
	private SubjectCategoryMapper subjectCategoryMapper;
	
	@Resource
	private SubjectMappingMapper subjectMappingMapper;
	
	@Resource
	private SubjectLabelMapper subjectLabelMapper;
	
	@Resource
	private PracticeSetDetailMapper practiceSetDetailMapper;
	
	@Resource
	private PracticeSetMapper practiceSetMapper;
	
	@Resource
	private SubjectMapper subjectMapper;
	
	@Resource
	private PracticeDetailMapper practiceDetailMapper;
	
	@Resource
	private PracticeMapper practiceMapper;
	
	@Resource
	private SubjectRadioMapper subjectRadioMapper;
	
	@Resource
	private SubjectMultipleMapper subjectMultipleMapper;
	
	@Override
	public List<SpecialPracticeVO> getSpecialPracticeContent() {
		List<SpecialPracticeVO> specialPracticeVOList = new LinkedList<>();
		List<Integer> subjectTypeList = new LinkedList<>();
		
		subjectTypeList.add(SubjectInfoTypeEnum.RADIO.getCode());
		subjectTypeList.add(SubjectInfoTypeEnum.MULTIPLE.getCode());
		subjectTypeList.add(SubjectInfoTypeEnum.JUDGE.getCode());
		
		CategoryDTO categoryDTO = new CategoryDTO();
		categoryDTO.setSubjectTypeList(subjectTypeList);
		
		List<PrimaryCategoryPO> poList = subjectCategoryMapper.getPrimaryCategory(categoryDTO);
		if (CollUtil.isEmpty(poList)) {
			return specialPracticeVOList;
		}
		
		poList.forEach(po -> {
			SpecialPracticeVO specialPracticeVO = new SpecialPracticeVO();
			specialPracticeVO.setPrimaryCategoryId(po.getParentId());
			
			CategoryPO categoryPO = subjectCategoryMapper.selectById(po.getParentId());
			
			specialPracticeVO.setPrimaryCategoryName(categoryPO.getCategoryName());
			
			CategoryDTO categoryDTOTmp = new CategoryDTO();
			
			categoryDTOTmp.setCategoryType(2);
			categoryDTOTmp.setParentId(po.getParentId());
			
			List<CategoryPO> smallPoList = subjectCategoryMapper.selectList(categoryDTOTmp);
			if (CollUtil.isEmpty(smallPoList)) {
				return;
			}
			
			List<SpecialPracticeCategoryVO> categoryList = new LinkedList<>();
			
			smallPoList.forEach(smallPo -> {
				List<SpecialPracticeLabelVO> labelVOList = getLabelVOList(smallPo.getId(), subjectTypeList);
				if (CollUtil.isEmpty(labelVOList)) {
					return;
				}
				
				SpecialPracticeCategoryVO specialPracticeCategoryVO = new SpecialPracticeCategoryVO();
				specialPracticeCategoryVO.setCategoryId(smallPo.getId());
				specialPracticeCategoryVO.setCategoryName(smallPo.getCategoryName());
				
				List<SpecialPracticeLabelVO> labelList = new LinkedList<>();
				labelVOList.forEach(labelVo -> {
					SpecialPracticeLabelVO specialPracticeLabelVO = new SpecialPracticeLabelVO();
					specialPracticeLabelVO.setId(labelVo.getId());
					specialPracticeLabelVO.setAssembleId(labelVo.getAssembleId());
					specialPracticeLabelVO.setLabelName(labelVo.getLabelName());
					labelList.add(specialPracticeLabelVO);
				});
				specialPracticeCategoryVO.setLabelList(labelList);
				categoryList.add(specialPracticeCategoryVO);
			});
			specialPracticeVO.setSpecialPracticeCategoryList(categoryList);
			specialPracticeVOList.add(specialPracticeVO);
		});
		return specialPracticeVOList;
	}
	
	private List<SpecialPracticeLabelVO> getLabelVOList(Long categoryId, List<Integer> subjectTypeList) {
		List<LabelCountPO> countPOList = subjectMappingMapper.getLabelSubjectCount(categoryId, subjectTypeList);
		if (CollUtil.isEmpty(countPOList)) {
			return CollUtil.newArrayList();
		}
		
		List<SpecialPracticeLabelVO> voList = new LinkedList<>();
		countPOList.forEach(countPO -> {
			SpecialPracticeLabelVO vo = new SpecialPracticeLabelVO();
			vo.setId(countPO.getLabelId());
			vo.setAssembleId(categoryId + "-" + countPO.getLabelId());
			
			SubjectLabelPO subjectLabelPO = subjectLabelMapper.queryById(countPO.getLabelId());
			
			vo.setLabelName(subjectLabelPO.getLabelName());
			
			voList.add(vo);
		});
		
		return voList;
	}
	
	// TODO: 事务可以只放在插入的地方，查询的地方可以不用事务
	@Override
	@Transactional(rollbackFor = Exception.class)
	public PracticeSetVO addPractice(PracticeSubjectDTO dto) {
		PracticeSetVO setVO = new PracticeSetVO();
		List<PracticeSubjectDetailVO> practiceList = getPracticeList(dto);
		if (CollUtil.isEmpty(practiceList)) {
			return setVO;
		}
		PracticeSetPO practiceSetPO = new PracticeSetPO();
		practiceSetPO.setSetType(1);
		List<String> assembleIds = dto.getAssembleIds();
		Set<Long> categoryIdSet = new HashSet<>();
		assembleIds.forEach(assembleId -> {
			Long categoryId = Long.valueOf(assembleId.split("-")[0]);
			categoryIdSet.add(categoryId);
		});
		StringBuffer setName = new StringBuffer();
		int i = 1;
		for (Long categoryId : categoryIdSet) {
			if (i > 2) {
				break;
			}
			CategoryPO categoryPO = subjectCategoryMapper.selectById(categoryId);
			setName.append(categoryPO.getCategoryName());
			setName.append("、");
			i = i + 1;
		}
		setName.deleteCharAt(setName.length() - 1);
		if (i == 2) {
			setName.append("专项练习");
		} else {
			setName.append("等专项练习");
		}
		practiceSetPO.setSetName(setName.toString());
		String labelId = assembleIds.get(0).split("-")[1];
		SubjectLabelPO labelPO = subjectLabelMapper.queryById(Long.valueOf(labelId));
		practiceSetPO.setPrimaryCategoryId(labelPO.getCategoryId());
		practiceSetPO.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());
		practiceSetPO.setCreatedBy(LoginUtil.getLoginId());
		practiceSetPO.setCreatedTime(LocalDateTime.now());
		practiceSetMapper.add(practiceSetPO);
		Long practiceSetId = practiceSetPO.getId();
		
		// TODO: 可以一次性插入
		practiceList.forEach(e -> {
			PracticeSetDetailPO detailPO = new PracticeSetDetailPO();
			detailPO.setSetId(practiceSetId);
			detailPO.setSubjectId(e.getSubjectId());
			detailPO.setSubjectType(e.getSubjectType());
			detailPO.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());
			detailPO.setCreatedBy(LoginUtil.getLoginId());
			detailPO.setCreatedTime(LocalDateTime.now());
			practiceSetDetailMapper.add(detailPO);
		});
		setVO.setSetId(practiceSetId);
		return setVO;
	}
	
	private List<PracticeSubjectDetailVO> getPracticeList(PracticeSubjectDTO dto) {
		List<PracticeSubjectDetailVO> practiceSubjectListVOS = new LinkedList<>();
		// 避免重复
		List<Long> excludeSubjectIds = new LinkedList<>();
		
		// 设置题目数量，之后优化到nacos动态配置
		Integer radioSubjectCount = 10;
		Integer multipleSubjectCount = 6;
		Integer judgeSubjectCount = 4;
		Integer totalSubjectCount = 20;
		// 查询单选
		dto.setSubjectCount(radioSubjectCount);
		dto.setSubjectType(SubjectInfoTypeEnum.RADIO.getCode());
		assembleList(dto, practiceSubjectListVOS, excludeSubjectIds);
		// 查询多选
		dto.setSubjectCount(multipleSubjectCount);
		dto.setSubjectType(SubjectInfoTypeEnum.MULTIPLE.getCode());
		assembleList(dto, practiceSubjectListVOS, excludeSubjectIds);
		// 查询判断
		dto.setSubjectCount(judgeSubjectCount);
		dto.setSubjectType(SubjectInfoTypeEnum.JUDGE.getCode());
		assembleList(dto, practiceSubjectListVOS, excludeSubjectIds);
		// 补充题目
		if (practiceSubjectListVOS.size() == totalSubjectCount) {
			return practiceSubjectListVOS;
		}
		Integer remainCount = totalSubjectCount - practiceSubjectListVOS.size();
		dto.setSubjectCount(remainCount);
		dto.setSubjectType(1);
		assembleList(dto, practiceSubjectListVOS, excludeSubjectIds);
		return practiceSubjectListVOS;
	}
	
	private List<PracticeSubjectDetailVO> assembleList(PracticeSubjectDTO dto, List<PracticeSubjectDetailVO> list,
	                                                   List<Long> excludeSubjectIds) {
		dto.setExcludeSubjectIds(excludeSubjectIds);
		List<SubjectPO> subjectPOList = subjectMapper.getPracticeSubject(dto);
		if (CollUtil.isEmpty(subjectPOList)) {
			return list;
		}
		subjectPOList.forEach(e -> {
			PracticeSubjectDetailVO vo = new PracticeSubjectDetailVO();
			vo.setSubjectId(e.getId());
			vo.setSubjectType(e.getSubjectType());
			excludeSubjectIds.add(e.getId());
			list.add(vo);
		});
		return list;
	}
	
	@Override
	public PracticeSubjectListVO getSubjects(GetPracticeSubjectsReq req) {
		Long setId = req.getSetId();
		PracticeSubjectListVO vo = new PracticeSubjectListVO();
		List<PracticeSubjectDetailVO> practiceSubjectListVOS = new LinkedList<>();
		List<PracticeSetDetailPO> practiceSetDetailPOS = practiceSetDetailMapper.selectBySetId(setId);
		if (CollUtil.isEmpty(practiceSetDetailPOS)) {
			return vo;
		}
		String loginId = LoginUtil.getLoginId();
		Long practiceId = req.getPracticeId();
		practiceSetDetailPOS.forEach(e -> {
			PracticeSubjectDetailVO practiceSubjectListVO = new PracticeSubjectDetailVO();
			practiceSubjectListVO.setSubjectId(e.getSubjectId());
			practiceSubjectListVO.setSubjectType(e.getSubjectType());
			if (ObjUtil.isNotNull(practiceId)) {
				PracticeDetailPO practiceDetailPO = practiceDetailMapper.selectDetail(practiceId, e.getSubjectId(), loginId);
				if (Objects.nonNull(practiceDetailPO) && StrUtil.isNotBlank(practiceDetailPO.getAnswerContent())) {
					practiceSubjectListVO.setIsAnswer(1);
				} else {
					practiceSubjectListVO.setIsAnswer(0);
				}
			}
			
			practiceSubjectListVOS.add(practiceSubjectListVO);
		});
		vo.setSubjectList(practiceSubjectListVOS);
		PracticeSetPO practiceSetPO = practiceSetMapper.selectById(setId);
		vo.setTitle(practiceSetPO.getSetName());
		if (ObjUtil.isNull(practiceId)) {
			Long newPracticeId = insertUnCompletePractice(setId);
			vo.setPracticeId(newPracticeId);
		} else {
			updateUnCompletePractice(practiceId);
			PracticePO practicePO = practiceMapper.selectById(practiceId);
			vo.setTimeUse(practicePO.getTimeUse());
			vo.setPracticeId(practiceId);
		}
		
		return vo;
	}
	
	private Long insertUnCompletePractice(Long practiceSetId) {
		PracticePO practicePO = new PracticePO();
		practicePO.setSetId(practiceSetId);
		practicePO.setCompleteStatus(CompleteStatusEnum.NO_COMPLETE.getCode());
		practicePO.setTimeUse("00:00:00");
		practicePO.setSubmitTime(LocalDateTime.now());
		practicePO.setCorrectRate(new BigDecimal("0.00"));
		practicePO.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());
		practicePO.setCreatedBy(LoginUtil.getLoginId());
		practicePO.setCreatedTime(LocalDateTime.now());
		practiceMapper.insert(practicePO);
		return practicePO.getId();
	}
	
	private void updateUnCompletePractice(Long practiceId) {
		PracticePO practicePO = new PracticePO();
		practicePO.setId(practiceId);
		practicePO.setSubmitTime(LocalDateTime.now());
		practiceMapper.update(practicePO);
	}
	
	@Override
	public PracticeSubjectVO getPracticeSubject(PracticeSubjectDTO dto) {
		PracticeSubjectVO practiceSubjectVO = new PracticeSubjectVO();
		SubjectPO subjectPO = subjectMapper.selectById(dto.getSubjectId());
		practiceSubjectVO.setSubjectName(subjectPO.getSubjectName());
		practiceSubjectVO.setSubjectType(subjectPO.getSubjectType());
		if (dto.getSubjectType() == SubjectInfoTypeEnum.RADIO.getCode()) {
			List<PracticeSubjectOptionVO> optionList = new LinkedList<>();
			List<SubjectRadioPO> radioSubjectPOS = subjectRadioMapper.selectBySubjectId(subjectPO.getId());
			radioSubjectPOS.forEach(e -> {
				PracticeSubjectOptionVO practiceSubjectOptionVO = new PracticeSubjectOptionVO();
				practiceSubjectOptionVO.setOptionContent(e.getOptionContent());
				practiceSubjectOptionVO.setOptionType(e.getOptionType());
				optionList.add(practiceSubjectOptionVO);
			});
			practiceSubjectVO.setOptionList(optionList);
		}
		if (dto.getSubjectType() == SubjectInfoTypeEnum.MULTIPLE.getCode()) {
			List<PracticeSubjectOptionVO> optionList = new LinkedList<>();
			List<SubjectMultiplePO> multipleSubjectPOS = subjectMultipleMapper.selectBySubjectId(subjectPO.getId());
			multipleSubjectPOS.forEach(e -> {
				PracticeSubjectOptionVO practiceSubjectOptionVO = new PracticeSubjectOptionVO();
				practiceSubjectOptionVO.setOptionContent(e.getOptionContent());
				practiceSubjectOptionVO.setOptionType(e.getOptionType());
				optionList.add(practiceSubjectOptionVO);
			});
			practiceSubjectVO.setOptionList(optionList);
		}
		return practiceSubjectVO;
	}
	
	@Override
	public PageResult<PracticeSetVO> getPreSetContent(PracticeSetDTO dto) {
		PageResult<PracticeSetVO> pageResult = new PageResult<>();
		PageInfo pageInfo = dto.getPageInfo();
		pageResult.setPageNo(pageInfo.getPageNo());
		pageResult.setPageSize(pageInfo.getPageSize());
		int start = (pageInfo.getPageNo() - 1) * pageInfo.getPageSize();
		long count = practiceSetMapper.getListCount(dto);
		if (count == 0) {
			return pageResult;
		}
		List<PracticeSetPO> setPOList = practiceSetMapper.getSetList(dto, start, dto.getPageInfo().getPageSize());
		if (log.isInfoEnabled()) {
			log.info("获取的模拟考卷列表{}", JSON.toJSONString(setPOList));
		}
		List<PracticeSetVO> list = new LinkedList<>();
		setPOList.forEach(e -> {
			PracticeSetVO vo = new PracticeSetVO();
			vo.setSetId(e.getId());
			vo.setSetName(e.getSetName());
			vo.setSetHeat(e.getSetHeat());
			vo.setSetDesc(e.getSetDesc());
			list.add(vo);
		});
		pageResult.setResult(list);
		pageResult.setTotal(count);
		return pageResult;
	}
	
	@Override
	public PageResult<UnCompletePracticeSetVO> getUnCompletePractice(GetUnCompletePracticeReq req) {
		PageResult<UnCompletePracticeSetVO> pageResult = new PageResult<>();
		PageInfo pageInfo = req.getPageInfo();
		pageResult.setPageNo(pageInfo.getPageNo());
		pageResult.setPageSize(pageInfo.getPageSize());
		int start = (pageInfo.getPageNo() - 1) * pageInfo.getPageSize();
		String loginId = LoginUtil.getLoginId();
		long count = practiceMapper.getUnCompleteCount(loginId);
		if (count == 0) {
			return pageResult;
		}
		List<PracticePO> poList = practiceMapper.getUnCompleteList(loginId, start, req.getPageInfo().getPageSize());
		if (log.isInfoEnabled()) {
			log.info("获取未完成的考卷列表{}", JSON.toJSONString(poList));
		}
		List<UnCompletePracticeSetVO> list = new LinkedList<>();
		poList.forEach(e -> {
			UnCompletePracticeSetVO vo = new UnCompletePracticeSetVO();
			vo.setSetId(e.getSetId());
			vo.setPracticeId(e.getId());
			vo.setPracticeTime(DateUtils.format(e.getSubmitTime(), "yyyy-MM-dd"));
			PracticeSetPO practiceSetPO = practiceSetMapper.selectById(e.getSetId());
			vo.setTitle(practiceSetPO.getSetName());
			list.add(vo);
		});
		pageResult.setResult(list);
		pageResult.setTotal(count);
		return pageResult;
	}
	
}